package info.openlvb.openlvg.server.ldap;

import info.openlvb.openlvg.client.base.*;
import info.openlvb.openlvg.server.log.FaceLog;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

public class LdapConnection {

	private static LdapConnection instance = null;
	private String LDAP_SERVER, GLOBAL_DC, ADMIN_DN, ADMIN_PWD;
	private Hashtable<String,String> ldapEnv;
	private DirContext ldapContext;
	private Properties prop;
	FaceLog log = FaceLog.getInstance();
	
	private LdapConnection(){
		log.message(log.INFO,"connexion au LDAP");
		prop = new Properties();
		try {
			FileInputStream in = new FileInputStream("ldap.properties");
			prop.load(in);
			in.close();
			LDAP_SERVER = prop.getProperty("ldap_server")+":"+prop.getProperty("ldap_port");
			GLOBAL_DC = prop.getProperty("ldap_global_dc");
			ADMIN_DN = prop.getProperty("ldap_admin_dn")+","+GLOBAL_DC;
			ADMIN_PWD = prop.getProperty("ldap_admin_password");
			log.message(log.INFO,LDAP_SERVER+"/"+GLOBAL_DC+"/"+ADMIN_DN+"/"+ADMIN_PWD);
			ldapEnv = new Hashtable<String,String>();
			ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
			ldapEnv.put(Context.PROVIDER_URL, "ldap://"+LDAP_SERVER);
			ldapEnv.put(Context.SECURITY_AUTHENTICATION,"simple");
			ldapEnv.put(Context.SECURITY_PRINCIPAL, ADMIN_DN);
			ldapEnv.put(Context.SECURITY_CREDENTIALS, ADMIN_PWD);
			ldapContext = new InitialDirContext(ldapEnv);
			
		} catch (FileNotFoundException e) {
			log.message(log.WARNING,"probleme de chargement de fichier - fichier non trouvé - de conf ldap");
			log.message(log.WARNING, FaceLog.printStackTrace(e));
		} catch (IOException e) {
			log.message(log.WARNING,"probleme de chargement de fichier de conf ldap : E/S");
			log.message(log.WARNING, FaceLog.printStackTrace(e));
		} catch (NamingException e) {
			log.message(log.WARNING,"prob de cnx au LDAP");
			log.message(log.WARNING, FaceLog.printStackTrace(e));
		}
		
	}
	public final static LdapConnection getInstance() {
		if(instance==null){
			instance = new LdapConnection(); 
			return instance;
		}else{
			return instance;
		}
	}
	/* uid : identifiant (ici guillaume.bordes = login)
	 * cn : nom de la personne
	 * givenname : prénom
	 * sn = nom + prenom
	 * gecos : description
	 */
	/**recherche dans l'annuaire LDAP un utilisateur par son uid
	 * 
	 * @param uid de l'utilisateur recherche
	 * 
	 * @return Account avec toutes les informations utilisateur
	 */
	public Account searchByUid(String uid){
		SearchControls controle = new SearchControls();
		/**Modification : Xavier
		 * Personnalisation par rapport à mon LDAP 
		 */
		String critere = "(uid="+uid+")";
		NamingEnumeration<SearchResult> e;
		try {
			e = ldapContext.search("ou=users,"+GLOBAL_DC, critere,controle);
			while(e.hasMore()){
				Account pers_cour = new Account();
				SearchResult r = e.next();
				try{
				/**Modification : Xavier
				 * Personnalisation par rapport à mon LDAP 
				 */
				//pers_cour.setPassword(r.getAttributes().get("sambaLMPassword").getAll().next().toString());
				pers_cour.setPassword(new String((byte[]) r.getAttributes().get("userPassword").get()));
				//pers_cour.setPassword(r.getAttributes().get("sambaLMPassword").getAll().next().toString());
				//pers_cour.setNom_complet(r.getAttributes().get("sn").getAll().next().toString());
				pers_cour.setLogin(r.getAttributes().get("uid").getAll().next().toString());
				pers_cour.setNom(r.getAttributes().get("cn").getAll().next().toString());
				//pers_cour.setPrenom(r.getAttributes().get("givenName").getAll().next().toString());
				pers_cour.setPrenom("prenom");
				//TODO : modifier par UserPassword !!
				//pers_cour.setDescription(r.getAttributes().get("gecos").getAll().next().toString());
				//System.out.println("userPassword"+r.getAttributes().get("userPassword").getAll().next().toString());
				//pers_cour.setPassword(r.getAttributes().get("userPassword").getAll().next().toString());
				return pers_cour;
				}catch(Exception ne){
					//MessageBox.alert("Un champs est manquant dans le compte utilisateur");
					log.message(log.WARNING, FaceLog.printStackTrace(ne));
				}
			}
		} catch (NamingException e1) {
			log.message(log.WARNING,"probleme searchByUid");
			log.message(log.WARNING, FaceLog.printStackTrace(e1));
		}
		return null;
	}
	
	/**
	 * recherche des utilisateurs appartenant à un groupe
	 * @author gbtux
	 * @param cn du groupe
	 * @return Group
	 * 
	 */
	public Group searchGroupByCn(String cn){
		
		SearchControls controle = new SearchControls();
		String critere = "(cn="+cn+")";
		NamingEnumeration<SearchResult> e;
		try {
			e = ldapContext.search("ou=Groups,"+GLOBAL_DC, critere,controle);
			while(e.hasMore()){
				Group group_cour = new Group();
				SearchResult r = e.next();
				group_cour.setCn(r.getAttributes().get("cn").getAll().next().toString());
				group_cour.setDescription(r.getAttributes().get("description").getAll().next().toString());
				group_cour.setDisplayname(r.getAttributes().get("displayName").getAll().next().toString());
				group_cour.setGid(r.getAttributes().get("gidNumber").getAll().next().toString());
				return group_cour;
			}
		} catch (NamingException e1) {
			log.message(log.WARNING, "probleme searchGroupByCn");
			log.message(log.WARNING, FaceLog.printStackTrace(e1));
		}
		
		return null;
	}
	/**
	 * recherche de tous les groupes dans le LDAP
	 * @author gbtux
	 * @param void
	 * @return liste des groupes
	 */
	public List<Group> searchGroups(){
			List<Group> lstgroup = new ArrayList<Group>();
			SearchControls controle = new SearchControls();
			String critere = "(cn=*)";
			NamingEnumeration<SearchResult> e;
			try {
				e = ldapContext.search("ou=Groups,"+GLOBAL_DC, critere,controle);
				while(e.hasMore()){
					Group group_cour = new Group();
					SearchResult r = e.next();
					group_cour.setCn(r.getAttributes().get("cn").getAll().next().toString());
					group_cour.setGid(r.getAttributes().get("gidNumber").getAll().next().toString());
					group_cour.setDescription(r.getAttributes().get("description").getAll().next().toString());
					try{
						Enumeration<?> values = r.getAttributes().get("memberUid").getAll();
						List<String> members = new ArrayList<String>();
						while(values.hasMoreElements()){
							members.add(values.nextElement().toString());
						}
						group_cour.setMembers((String[]) members.toArray());
						
					}catch(Exception e2){
						log.message(log.INFO, "pas de membre");
					}
					lstgroup.add(group_cour);
				}
				return lstgroup;
			} catch (NamingException e1) {
				log.message(log.WARNING, "probleme searchGroups");
				log.message(log.WARNING, FaceLog.printStackTrace(e1));
			}
			return null;
		}
	/**
	 * recherche de tous les membres d'un groupe
	 * @author gbtux
	 * @param cn du groupe
	 * @return 
	 */
	public List<Account> findMembersByCnOfGroup(String cn) {
		List<Account> lstpers = new ArrayList<Account>();
		SearchControls controle = new SearchControls();
		String critere = "(cn="+cn+")";
		NamingEnumeration<SearchResult> e;
		try {
			e = ldapContext.search("ou=Groups,"+GLOBAL_DC, critere,controle);
			while(e.hasMore()){
				SearchResult r = e.next();
				try{
					Enumeration<?> values = r.getAttributes().get("memberUid").getAll();
					while(values.hasMoreElements()){
						lstpers.add(searchByUid(values.nextElement().toString()));
					}
					
				}catch(Exception w){
					log.message(log.WARNING, "pas de members dans le groupe : "+cn);
					log.message(log.WARNING, FaceLog.printStackTrace(w));
				}
			}
			return lstpers;
		} catch (NamingException e1) {
			log.message(log.WARNING, "probleme searchGroups");
			log.message(log.WARNING, FaceLog.printStackTrace(e1));
		}
		return null;
	}
	
	/**
	 * ajout d'un nouveau groupe dans le LDAP
	 * @author gbtux
	 * @param Group
	 * @return void
	 */
	public void addGroup(Group group){
		String dn = "cn="+group.getCn()+",ou=Groups,"+GLOBAL_DC;
		Attributes attributes = new BasicAttributes(true);
		
		Attribute attr_obj7 = new BasicAttribute("objectClass");
		attr_obj7.add("posixGroup");
		attributes.put(attr_obj7);
		Attribute attr_obj6 = new BasicAttribute("gidNumber");
		attr_obj6.add(nextgid()); 
		attributes.put(attr_obj6);
		Attribute attr_desc = new BasicAttribute("description");
		attr_desc.add(group.getDescription());
		attributes.put(attr_desc);
		
		try {
			ldapContext.createSubcontext(dn, attributes);
			BasicAttribute newAtt1 = new BasicAttribute("objectClass","sambaGroupMapping");
			BasicAttribute newAtt2 = new BasicAttribute("sambaGroupType","2");
			BasicAttribute newAtt3 = new BasicAttribute("sambaSID",getSID());
			BasicAttribute newAtt4 = new BasicAttribute("displayName",group.getCn());
			ModificationItem[] modItems = new ModificationItem[4];
			modItems[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE,newAtt1);
			modItems[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE,newAtt2);
			modItems[2] = new ModificationItem(DirContext.ADD_ATTRIBUTE,newAtt3);
			modItems[3] = new ModificationItem(DirContext.ADD_ATTRIBUTE,newAtt4);
			ldapContext.modifyAttributes(dn, modItems);
			
			ModificationItem[] modItemsMembers = new ModificationItem[group.getMembers().length];
			for(int i=0;i<group.getMembers().length;i++){
				Attribute attr_member = new BasicAttribute("memberUid");
				attr_member.add(group.getMembers()[i]);
				modItemsMembers[i] = new ModificationItem(DirContext.ADD_ATTRIBUTE,attr_member);
			}
			ldapContext.modifyAttributes(dn, modItemsMembers);
		} catch (NamingException e) {
			log.message(log.WARNING, "probleme addGroup");
			log.message(log.WARNING, FaceLog.printStackTrace(e));
		}
	}
	private String getSID() {
		String critere = "(sambaDomainName=OPENLVB.INFO)";
		SearchControls controle = new SearchControls();
		NamingEnumeration<SearchResult> e;
		String SID="";
		try {
			e = ldapContext.search(GLOBAL_DC, critere,controle);
			while(e.hasMore()){
				SearchResult r = e.next();
				SID = r.getAttributes().get("sambaSID").getAll().next().toString();
				String GID = r.getAttributes().get("gidNumber").getAll().next().toString();
				int intGID = Integer.parseInt(GID);
				intGID--;
				GID = String.valueOf(intGID);
				SID+="-"+GID;

				return SID;
			}
		}catch(Exception ex){
			log.message(log.WARNING, "probleme getSID");
			log.message(log.WARNING, FaceLog.printStackTrace(ex));
		}
		return null;
	}
	private String nextgid() {
		String critere = "(sambaDomainName=OPENLVB.INFO)";
		SearchControls controle = new SearchControls();
		NamingEnumeration<SearchResult> e;
		String nextgid="";
		try {
			e = ldapContext.search(GLOBAL_DC, critere,controle);
			while(e.hasMore()){
				SearchResult r = e.next();
				nextgid = r.getAttributes().get("gidNumber").getAll().next().toString();
			}
			incrementgid(nextgid);
			log.message(log.INFO, "nextgid : "+nextgid);
			return nextgid;
		}catch(Exception ex){
			log.message(log.WARNING, "probleme nexgid");
			log.message(log.WARNING, FaceLog.printStackTrace(ex));
		}
		return null;
	}
	private void incrementgid(String nextgid) {

		String dn = "sambaDomainName=OPENLVB.INFO,"+GLOBAL_DC;
		int newgid = Integer.parseInt(nextgid);
		newgid++;
		BasicAttribute gidAtt = new BasicAttribute("gidNumber",String.valueOf(newgid));
		Attributes attributes = new BasicAttributes(true);
		attributes.put(gidAtt);
		try{
			ldapContext.modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, attributes);
		}catch(Exception exc){
			log.message(log.WARNING, "probleme incrementation gidNumber");
			log.message(log.WARNING, FaceLog.printStackTrace(exc));
		}

	}
	public List<String> searchUsersUid(){
		SearchControls controle = new SearchControls();
		String critere = "(uid=*)";
		NamingEnumeration<SearchResult> e;
		try {
			e = ldapContext.search("ou=users,"+GLOBAL_DC, critere,controle);
			List<String> users = new ArrayList<String>();
			while(e.hasMore()){
				SearchResult r = e.next();
				users.add(r.getAttributes().get("uid").getAll().next().toString());
			}
			return users;
		}catch(Exception e1){
			log.message(log.WARNING, "probleme searchUsersUid");
			log.message(log.WARNING, FaceLog.printStackTrace(e1));
		}
		return null;
	}
	public void deleteGroup(String group) {
		// TODO Auto-generated method stub
		
	}
	public void addUserInGroup(String group, String userUid) {
		// TODO Auto-generated method stub
		
	}
	public void removeUserInGroup(String cnOfGroup, String login) {
		// TODO Auto-generated method stub
		
	}
	/**
	 * ajout d'un utilisateur
	 */
	public void addUser(Account account){
		String dn = "uid="+account.getLogin()+",ou=users,"+GLOBAL_DC;
		Attributes attributes = new BasicAttributes(true);

		Attribute attr_obj7 = new BasicAttribute("objectClass");
		attr_obj7.add("inetOrgPerson"); //avant openlvb
		attributes.put(attr_obj7);

		Attribute attr_cn = new BasicAttribute("cn");
		attr_cn.add(account.getNom());
		attributes.put(attr_cn);

		Attribute attr_sn = new BasicAttribute("sn");
		attr_sn.add(account.getNom());
		attributes.put(attr_sn);

		Attribute attr_givenname = new BasicAttribute("givenName");
		attr_givenname.add(account.getPrenom());//
		attributes.put(attr_givenname);

		Attribute attr_uid = new BasicAttribute("uid");
		attr_uid.add(account.getLogin());
		attributes.put(attr_uid);

		Attribute attr_pass = new BasicAttribute("userPassword");
		SSHA ssha = new SSHA("SHA");
		attr_pass.add(ssha.createDigest(account.getPassword()));
		attributes.put(attr_pass);

		Attribute attr_mail = new BasicAttribute("mail");
		attr_mail.add(account.getMail());
		attributes.put(attr_mail);
		
		try {
			ldapContext.createSubcontext(dn, attributes);
			//partie à ne pas toucher
			//en effet, on ne peut ajouter en même temps les 2 objectclass openlvb 
			//(dérivé de inetOrgperson) et posixAccount
			BasicAttribute newAtt = new BasicAttribute("objectClass","posixAccount");
			ModificationItem[] modItems = new ModificationItem[6];
			modItems[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE,newAtt);
			Attribute attr_shell = new BasicAttribute("loginShell");
			attr_shell.add(account.getShell());
			modItems[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE,attr_shell);

			Attribute attr_gecos = new BasicAttribute("gecos");
			attr_gecos.add(account.getNom_complet());
			modItems[2] = new ModificationItem(DirContext.ADD_ATTRIBUTE,attr_gecos);

			Attribute attr_uid_number = new BasicAttribute("uidNumber");
			attr_uid_number.add(nextuid()); 
			modItems[3] = new ModificationItem(DirContext.ADD_ATTRIBUTE,attr_uid_number);

			Attribute attr_gid_number = new BasicAttribute("gidNumber");
			attr_gid_number.add("513");
			modItems[4] = new ModificationItem(DirContext.ADD_ATTRIBUTE,attr_gid_number);

			Attribute attr_home = new BasicAttribute("homeDirectory");
			attr_home.add("/home/"+account.getLogin());
			modItems[5] = new ModificationItem(DirContext.ADD_ATTRIBUTE,attr_home);

			ldapContext.modifyAttributes(dn, modItems);
			//fin do not touch
			
		} catch (NamingException ne) {
			log.message(log.WARNING, "probleme addUser");
			log.message(log.WARNING, FaceLog.printStackTrace(ne));
		}
	}
	private String nextuid() {
		String critere = "(sambaDomainName=OPENLVB.INFO)";
		SearchControls controle = new SearchControls();
		NamingEnumeration<SearchResult> e;
		String nextuid="";
		try {
			e = ldapContext.search(GLOBAL_DC, critere,controle);
			while(e.hasMore()){
				SearchResult r = e.next();
				nextuid = r.getAttributes().get("uidNumber").getAll().next().toString();
			}
			incrementuid(nextuid);
			log.message(log.INFO, "nextgid : "+nextuid);
			return nextuid;
		}catch(Exception ex){
			log.message(log.WARNING, "probleme nextuid");
			log.message(log.WARNING, FaceLog.printStackTrace(ex));
		}
		return null;
	}
	private void incrementuid(String nextuid) {

		String dn = "sambaDomainName=OPENLVB.INFO,"+GLOBAL_DC;
		int newuid = Integer.parseInt(nextuid);
		newuid++;
		BasicAttribute uidAtt = new BasicAttribute("uidNumber",String.valueOf(newuid));
		Attributes attributes = new BasicAttributes(true);
		attributes.put(uidAtt);
		try{
			ldapContext.modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, attributes);
		}catch(Exception exc){
			log.message(log.WARNING, "probleme incrementation uidNumber");
			log.message(log.WARNING, FaceLog.printStackTrace(exc));
		}

	}
	/**
	 * modification d'un groupe
	 */
	public void modifyGroup(String oldcn, String name, String description){
		BasicAttribute descAtt = new BasicAttribute("description", description);
		BasicAttribute displayAtt = new BasicAttribute("displayName", name);
		Attributes attributes = new BasicAttributes(true);
		attributes.put(descAtt);
		attributes.put(displayAtt);

		if(oldcn.equals(name)){
			String dn = "cn="+oldcn+",ou=Groups,"+GLOBAL_DC;
			try{
				ldapContext.modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, attributes);
			}catch(Exception exc){
				log.message(log.WARNING, "probleme modifyGroup sans modif dn");
				log.message(log.WARNING, FaceLog.printStackTrace(exc));
			}
		}else{
			String olddn = "cn="+oldcn+",ou=Groups,"+GLOBAL_DC;
			String newdn = "cn="+name+",ou=Groups,"+GLOBAL_DC;
			try{
				ldapContext.rename(olddn, newdn);
				ldapContext.modifyAttributes(newdn, DirContext.REPLACE_ATTRIBUTE, attributes);
			}catch(Exception exc){
				log.message(log.WARNING, "probleme modifyGroup");
				log.message(log.WARNING, FaceLog.printStackTrace(exc));
			}
		}
	}
	/**
	 * recherche de tous les utilisateurs
	 */
	public List<Account> searchUsers(){
		List<Account> lstusers = new ArrayList<Account>();
		SearchControls controle = new SearchControls();
		String critere = "(uid=*)";
		NamingEnumeration<SearchResult> e;
		try {
			e = ldapContext.search("ou=users,"+GLOBAL_DC, critere,controle);
			while(e.hasMore()){
				SearchResult r = e.next();
				int uidNumber=Integer.parseInt(r.getAttributes().get("uidNumber").getAll().next().toString());
				if(uidNumber>=1000	&& uidNumber < 65000){
					Account cour = new Account();
					cour.setDescription(r.getAttributes().get("uid").getAll().next().toString());
					//ici on va chercher un des champs nécessaires a samba
					//si on trouve vrai
					cour.setSambaEnabled(false);
					NamingEnumeration<?> objectclass = r.getAttributes().get("objectClass").getAll();
					while(objectclass.hasMore()){
						String objectC = (String) objectclass.next();
						if(objectC.equals("sambaSamAccount")){
							cour.setSambaEnabled(true);
							cour.setHome(r.getAttributes().get("homeDirectory").getAll().next().toString());
						}
					}
					cour.setGroupe_principal(r.getAttributes().get("gidNumber").getAll().next().toString());
					cour.setLogin(r.getAttributes().get("uid").getAll().next().toString());
					try{
						cour.setMail(r.getAttributes().get("mail").getAll().next().toString());
					}catch(Exception en){
						cour.setMail("");
					}
					cour.setNom(r.getAttributes().get("cn").getAll().next().toString());
					cour.setNom_complet(r.getAttributes().get("gecos").getAll().next().toString());
					cour.setPassword(r.getAttributes().get("userPassword").getAll().next().toString());
					cour.setPrenom(r.getAttributes().get("givenName").getAll().next().toString());
					cour.setShell(r.getAttributes().get("loginShell").getAll().next().toString());
					lstusers.add(cour);
				}
			}
			return lstusers;
		}catch(Exception ex){
			log.message(log.WARNING, "probleme searchUsers");
			log.message(log.WARNING, FaceLog.printStackTrace(ex));
		}
		return null;
	}

}
